/*
整合chrdev_region(dev_t)，cdev，sysfs class(struct class)，sysfs device(struct device)

安装此模块，将
    在/proc/devices文件里生成设备(sysfs_dev)及其主设备号
    在/sys/class/生成sysfs_class目录
    在/sys/class/sysfs_class/下生成lxz-sysfs-device设备节点(文件夹)
    在/dev/下生成lxz-sysfs-device设备节点
*/

#include "linux/init.h"
#include "linux/module.h"
#include "linux/device.h"
#include "linux/fs.h"

static dev_t sysfs_dev_id = 0;
static uint32_t sysfs_dev_count = 1;
static const char *sysfs_dev_name = "sysfs_dev";
static const char *sysfs_class_name = "sysfs_class";
static struct class *sysfs_class = NULL;
static struct device *sysfs_device = NULL;
static const char *sysfs_device_name = "lxz-sysfs-device";

static int __init sysfs_device_init(void)
{
    int ret = -1;

    printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    /*
    分配字符设备号并注册字符设备号
    int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
			const char *name)
    */
    ret = alloc_chrdev_region(&sysfs_dev_id, 0, sysfs_dev_count, sysfs_dev_name);
    if (ret < 0)
    {
        printk("alloc_chrdev_region error\n");
        return -1;
    }
    printk("alloc_chrdev_region succeed,MAJOR=%u,MINOR=%u\n", MAJOR(sysfs_dev_id), MINOR(sysfs_dev_id));

    // 创建sysfs设备类别
    sysfs_class = class_create(THIS_MODULE, sysfs_class_name);
    if (IS_ERR(sysfs_class))
    {
        printk("class_create error\n");

        unregister_chrdev_region(sysfs_dev_id, sysfs_dev_count);
        return -1;
    }
    printk("class_create succeed\n");

    // 创建基于设备号和sysfs设备类别的设备，将导出到/sys/class/
    sysfs_device = device_create(sysfs_class, NULL, sysfs_dev_id, NULL, sysfs_device_name);
    if (IS_ERR(sysfs_device))
    {
        printk("device_create error\n");

        class_destroy(sysfs_class);
        unregister_chrdev_region(sysfs_dev_id, sysfs_dev_count);
        return -1;
    }
    printk("device_create succeed\n");

    return 0;
}

static void __exit sysfs_device_exit(void)
{
    printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    device_destroy(sysfs_class, sysfs_dev_id);

    class_destroy(sysfs_class);

    unregister_chrdev_region(sysfs_dev_id, sysfs_dev_count);
}

module_init(sysfs_device_init);
module_exit(sysfs_device_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lxz");
